/**
 * 文件名称: gpio.c
 * 摘    要: linux应用程序下操作gpio接口源文件
 * 来    源: 参考 http://bbs.myir-tech.com/thread-7183-1-1.html
 *
 * 当前版本: 1.0 
 * 作    者: huenrong
 * 完成日期: 2019年05月12日
 **/


#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <poll.h>

#include "gpio.h"

#define     SYSFS_GPIO_DIR      "/sys/class/gpio" // gpio路径
#define     MAX_BUF             60


/************************************************************************
函数名称: gpio_export
函数功能: 导出gpio到用户空间
函数参数: gpio: 需要导出的gpio编号
函数返回: 0: 成功
          -1: 失败
************************************************************************/
int gpio_export(const unsigned int gpio)
{
    int fd = -1;        // 文件描述符
    int len = 0;
    int ret = -1;       // 函数返回值
    char buf[MAX_BUF] = {0};

    fd = open(SYSFS_GPIO_DIR "/export", O_WRONLY);
    if (fd < 0)
    {
        perror("gpio_export");
        return -1;
    }

    len = snprintf(buf, sizeof(buf), "%d", gpio);
    ret = write(fd, buf, len);
    if (-1 == ret)
    {
        close(fd);
        return -1;
    }

    ret = close(fd);
    if (-1 == ret)
    {
        return -1;
    }

    return 0;
}

/************************************************************************
函数名称: gpio_unexport
函数功能: 取消导出到用户空间的gpio
函数参数: gpio: 导出的gpio编号
函数返回: 0: 成功
          -1: 失败
************************************************************************/
int gpio_unexport(const unsigned int gpio)
{
    int fd = -1;        // 文件描述符
    int len = 0;
    int ret = -1;       // 函数返回值
    char buf[MAX_BUF] = {0};

    fd = open(SYSFS_GPIO_DIR "/unexport", O_WRONLY);
    if (fd < 0)
    {
        perror("gpio_unexport");
        return -1;
    }

    len = snprintf(buf, sizeof(buf), "%d", gpio);
    ret = write(fd, buf, len);
    if (-1 == ret)
    {
        close(fd);
        return -1;
    }

    ret = close(fd);
    if (-1 == ret)
    {
        return -1;
    }

    return 0;
}

/************************************************************************
函数名称: gpio_set_direction
函数功能: 设置gpio方向
函数参数: gpio: 导出的gpio编号
          out_flag: 输入/输出标志(0: in, 1: out)
函数返回: 0: 成功
          -1: 失败
************************************************************************/
int gpio_set_direction(const unsigned int gpio, const int out_flag)
{
    int fd = -1;        // 文件描述符
    int len = 0;
    int ret = -1;       // 函数返回值
    char buf[MAX_BUF] = {0};

    len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/direction", gpio);
    fd = open(buf, O_WRONLY);
    if (fd < 0)
    {
        perror("gpio_set_direction");
        return -1;
    }

    if (0 == out_flag)
    {
        ret = write(fd, "in", 3);
        if (-1 == ret)
        {
            close(fd);
            return -1;
        }
    }
    else if (1 == out_flag)
    {
        ret = write(fd, "out", 4);
        if (-1 == ret)
        {
            close(fd);
            return -1;
        }
    }
    else
    {
        return -1;
    }

    ret = close(fd);
    if (-1 == ret)
    {
        return -1;
    }

    return 0;
}

/************************************************************************
函数名称: gpio_set_value
函数功能: 设置gpio输出电平值
函数参数: gpio: 导出的gpio编号
          gpio_value: gpio的值(只能是0或者1)
函数返回: 0: 成功
          -1: 失败
************************************************************************/
int gpio_set_value(const unsigned int gpio, const unsigned int gpio_value)
{
    int fd = -1;        // 文件描述符
    int len = 0;
    int ret = -1;       // 函数返回值
    char buf[MAX_BUF] = {0};

    len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);
    fd = open(buf, O_WRONLY);
    if (fd < 0)
    {
        perror("gpio_set_value");
        return -1;
    }

    if (0 == gpio_value)
    {
        write(fd, "0", 2);
        if (-1 == ret)
        {
            close(fd);
            return -1;
        }
    }
    else if (1 == gpio_value)
    {
        write(fd, "1", 2);
        if (-1 == ret)
        {
            close(fd);
            return -1;
        }
    }
    else
    {
        return -1;
    }
    
    ret = close(fd);
    if (-1 == ret)
    {
        return -1;
    }

    return 0;
}

/************************************************************************
函数名称: gpio_get_value
函数功能: 获取gpio的电平值
函数参数: gpio: 导出的gpio编号
          gpio_value: 获取到的gpio电平值(正常情况是0或者1, 若获取到的值错误, 则是2)
函数返回: 0: 成功
          -1: 失败
************************************************************************/
int gpio_get_value(const unsigned int gpio, unsigned int *gpio_value)
{
    int fd = -1;        // 文件描述符
    int len = 0;
    int ret = -1;       // 函数返回值
    char ch = 0;        // 获取到的gpio电平值
    char buf[MAX_BUF] = {0};

    len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);
    fd = open(buf, O_RDONLY);
    if (fd < 0)
    {
        perror("gpio_get_value");
        return -1;
    }

    ret = read(fd, &ch, 1);
    if (-1 == ret)
    {
        close(fd);
        return -1;
    }

    if ('0' == ch)
    {
        *gpio_value = 0;
    }
    else if ('1' == ch)
    {
        *gpio_value = 1;
    }
    else
    {
        *gpio_value = 2;
    }

    ret = close(fd);
    if (-1 == ret)
    {
        return -1;
    }

    return 0;
}

/************************************************************************
函数名称: gpio_set_edge
函数功能: 设置触发中断的边沿
函数参数: gpio: 需要设置的的gpio编号
          edge: 中断触发边沿(可选值: "none", "rising", "falling", "both")
函数返回: 0: 成功
          -1: 失败
************************************************************************/
int gpio_set_edge(const unsigned int gpio, const char *edge)
{
    int fd = -1;
    int ret = -1;
    char buf[MAX_BUF] = {0};

    // 边沿设置错误
    if (!(strcmp(edge, "rising")) && !(strcmp(edge, "falling")) && 
        !(strcmp(edge, "none")) && !(strcmp(edge, "both")))
    {
        printf("edge parameter error");
        
        return -1;
    }

    ret = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/edge", gpio);
    fd = open(buf, O_WRONLY);
    if (fd < 0)
    {
        perror("gpio_set_edge");
        
        return -1;
    }

    ret = write(fd, edge, strlen(edge) + 1);
    if (-1 == ret)
    {
        close(fd);

        return -1;
    }
    
    close(fd);
    
    return 0;
}

/************************************************************************
函数名称: gpio_fd_open
函数功能: 打开gpio设备并返回文件描述符
函数参数: gpio: gpio编号
函数返回: 成功: 返回文件描述符
          失败: 返回-1
************************************************************************/
int gpio_fd_open(const unsigned int gpio)
{  
    int fd = -1;
    char buf[MAX_BUF] = {0};

    snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);

    fd = open(buf, O_RDONLY | O_NONBLOCK );
    if (fd < 0)
    {
        perror("gpio/fd_open");
        
        return -1;
    }

    return fd;
}

/************************************************************************
函数名称: gpio_fd_open
函数功能: 关闭gpio设备
函数参数: fd: gpio文件描述符
函数返回: 0: 成功
          -1: 失败
************************************************************************/
int gpio_fd_close(const int fd)
{  
    return close(fd);
}


